'
' *   WakeOnLAN - Wake On LAN
' *    Copyright (C) 2004-2018 Aquila Technology, LLC. <webmaster@aquilatech.com>
' *
' *    This file is part of WakeOnLAN.
' *
' *    WakeOnLAN is free software: you can redistribute it and/or modify
' *    it under the terms of the GNU General Public License as published by
' *    the Free Software Foundation, either version 3 of the License, or
' *    (at your option) any later version.
' *
' *    WakeOnLAN is distributed in the hope that it will be useful,
' *    but WITHOUT ANY WARRANTY; without even the implied warranty of
' *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
' *    GNU General Public License for more details.
' *
' *    You should have received a copy of the GNU General Public License
' *    along with WakeOnLAN.  If not, see <http://www.gnu.org/licenses/>.
' 


'
' *    This module originated from https://autoupdaterdotnet.codeplex.com/
' 

Imports System
Imports Microsoft.Win32
Imports System.ComponentModel
Imports System.Globalization
Imports System.IO
Imports System.Net
Imports System.Net.Cache
Imports System.Reflection
Imports System.Threading
Imports System.Windows.Forms
Imports System.Xml

Public Class AutoUpdateEventArgs
	Inherits EventArgs
	Public Enum StatusCodes
		idle
		checking
		updateAvailable
		noUpdateAvailable
		delayed
		[error]
	End Enum

	Public Status As StatusCodes
	Public Text As String
End Class

''' <summary>
''' Main class that lets you auto update applications by setting some static fields and executing its Start method.
''' </summary>
Public NotInheritable Class AutoUpdater
	Private Sub New()
	End Sub
    Friend Shared DialogTitle As String
    Friend Shared ChangeLogURL As String
    Friend Shared DownloadURL As String
    Friend Shared RegistryLocation As String
    Friend Shared AppTitle As String
    Friend Shared CurrentVersion As Version
    Friend Shared InstalledVersion As Version

    Public Delegate Sub UpdateStatusHandler(sender As Object, e As AutoUpdateEventArgs)
    Public Shared Event UpdateStatus As UpdateStatusHandler

    Public Shared Sub OnUpdateStatus(e As AutoUpdateEventArgs)
        RaiseEvent UpdateStatus(Nothing, e)
    End Sub

    ''' <summary>
    ''' URL of the xml file that contains information about latest version of the application.
    ''' </summary>
    ''' 
    Public Shared AppCastURL As String

    ''' <summary>
    ''' Opens the download url in default browser if true. Very useful if you have portable application.
    ''' </summary>
    Public Shared OpenDownloadPage As Boolean = False

    ''' <summary>
    ''' Sets the current culture of the auto update notification window. Set this value if your application supports functionalty to change the languge of the application.
    ''' </summary>
    Public Shared CurrentCulture As CultureInfo

    ''' <summary>
    ''' If this is true users see dialog where they can set remind later interval otherwise it will take the interval from RemindLaterAt and RemindLaterTimeSpan fields.
    ''' </summary>
    Public Shared LetUserSelectRemindLater As Boolean = True

    ''' <summary>
    ''' Remind Later interval after user should be reminded of update.
    ''' </summary>
    Public Shared RemindLaterAt As Integer = 2

    ''' <summary>
    ''' Set if RemindLaterAt interval should be in Minutes, Hours or Days.
    ''' </summary>
    Public Shared RemindLaterTimeSpan As RemindLaterFormat = RemindLaterFormat.Days

    ''' <summary>
    ''' Read this webpage to get extended version information.
    ''' </summary>
    Public Shared versionURL As String
    Public Shared versionResult As String

    Public Shared force As Boolean = False

    Public Enum RemindLaterFormat
        Minutes
        Hours
        Days
    End Enum

    ''' <summary>
    ''' Start checking for new version of application and display dialog to the user if update is available.
    ''' </summary>
    ''' <param name="days">Number of days to wait between automatic checks.</param>
    Public Shared Sub Start(days As Integer)
        Dim backgroundWorker As New BackgroundWorker()
        AddHandler backgroundWorker.DoWork, AddressOf BackgroundWorkerDoWork
        backgroundWorker.RunWorkerAsync(days)
    End Sub

    Private Shared Sub BackgroundWorkerDoWork(sender As Object, e As DoWorkEventArgs)
        Dim compareResult As Integer
        Dim args As New AutoUpdateEventArgs()

        Thread.CurrentThread.CurrentUICulture = CurrentCulture
        Dim mainAssembly = Assembly.GetEntryAssembly()
        Dim companyAttribute = DirectCast(GetAttribute(mainAssembly, GetType(AssemblyCompanyAttribute)), AssemblyCompanyAttribute)
        Dim titleAttribute = DirectCast(GetAttribute(mainAssembly, GetType(AssemblyTitleAttribute)), AssemblyTitleAttribute)
        AppTitle = If(titleAttribute IsNot Nothing, titleAttribute.Title, mainAssembly.GetName().Name)
        Dim appCompany = If(companyAttribute IsNot Nothing, companyAttribute.Company, "")
        Dim days As Integer = CInt(e.Argument)
        RegistryLocation = String.Format("Software\{0}\{1}\AutoUpdater", appCompany, AppTitle)
        Dim updateKey As RegistryKey = If(Registry.CurrentUser.OpenSubKey(RegistryLocation, True), Registry.CurrentUser.CreateSubKey(RegistryLocation))

        Dim remindLaterTime As Object = updateKey.GetValue("remindlater")
        If remindLaterTime Is Nothing AndAlso days > 0 Then
            Dim lastcheck As String = updateKey.GetValue("lastcheck", "").ToString()
            If Not String.IsNullOrEmpty(lastcheck) Then
                Try
                    Dim lastcheckDate As DateTime = Convert.ToDateTime(lastcheck, CultureInfo.InvariantCulture)
                    compareResult = DateTime.Compare(DateTime.Now, lastcheckDate.AddDays(days))
                    If compareResult < 0 Then
                        args.Status = AutoUpdateEventArgs.StatusCodes.delayed
                        args.Text = String.Format(Resources.sLastCheck, lastcheckDate.ToString("D", CurrentCulture))
                        OnUpdateStatus(args)
                        Return
                    End If
                Catch generatedExceptionName As Exception
                End Try
            End If
        End If

        If remindLaterTime IsNot Nothing AndAlso days > 0 Then
            Try
                Dim remindLater As DateTime = Convert.ToDateTime(remindLaterTime.ToString(), CultureInfo.InvariantCulture)
                compareResult = DateTime.Compare(DateTime.Now, remindLater)
                If compareResult < 0 Then
                    Dim updateForm = New UpdateForm(True)
                    updateForm.SetTimer(remindLater)
                    Return
                End If
            Catch generatedExceptionName As Exception
            End Try
        End If
        If remindLaterTime IsNot Nothing Then
            updateKey.DeleteValue("remindlater")
        End If
        InstalledVersion = mainAssembly.GetName().Version
        args.Status = AutoUpdateEventArgs.StatusCodes.checking
        args.Text = Resources.sCheckingForUpdates
        OnUpdateStatus(args)
        Dim webRequest__1 As WebRequest = WebRequest.Create(AppCastURL)
        webRequest__1.CachePolicy = New HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore)
        Dim webResponse As WebResponse
        Try
            webResponse = webRequest__1.GetResponse()
        Catch ex As Exception
            args.Status = AutoUpdateEventArgs.StatusCodes.error
            args.Text = ex.Message
            OnUpdateStatus(args)
            Return
        End Try
        Dim appCastStream As Stream = webResponse.GetResponseStream()
        Dim receivedAppCastDocument As New XmlDocument()
        If appCastStream IsNot Nothing Then
            receivedAppCastDocument.Load(appCastStream)
        Else
            Return
        End If
        Dim appCastItems As XmlNodeList = receivedAppCastDocument.SelectNodes("channel/item")
        If appCastItems IsNot Nothing Then
            For Each item As XmlNode In appCastItems
                Dim appCastVersion As XmlNode = item.SelectSingleNode("version")
                If appCastVersion IsNot Nothing Then
                    Dim appVersion As String = appCastVersion.InnerText
                    Dim version = New Version(appVersion)
                    If version <= InstalledVersion Then
                        Continue For
                    End If
                    CurrentVersion = version
                Else
                    Continue For
                End If
                Dim appCastTitle As XmlNode = item.SelectSingleNode("title")
                DialogTitle = If(appCastTitle IsNot Nothing, appCastTitle.InnerText, "")
                Dim appCastChangeLog As XmlNode = item.SelectSingleNode("changelog")
                ChangeLogURL = If(appCastChangeLog IsNot Nothing, appCastChangeLog.InnerText, "")
                Dim appCastUrl__2 As XmlNode = item.SelectSingleNode("url")
                DownloadURL = If(appCastUrl__2 IsNot Nothing, appCastUrl__2.InnerText, "")
            Next
        End If

        Dim skip As Object = updateKey.GetValue("skip")
        Dim applicationVersion As Object = updateKey.GetValue("version")
        If skip IsNot Nothing AndAlso applicationVersion IsNot Nothing Then
            Dim skipValue As String = skip.ToString()
            Dim skipVersion = New Version(applicationVersion.ToString())
            If skipValue.Equals("1") AndAlso CurrentVersion <= skipVersion Then
                args.Status = AutoUpdateEventArgs.StatusCodes.noUpdateAvailable
                args.Text = Resources.sSkipping
                OnUpdateStatus(args)
                Return
            End If
            If CurrentVersion > skipVersion Then
                Dim updateKeyWrite As RegistryKey = Registry.CurrentUser.CreateSubKey(RegistryLocation)
                If updateKeyWrite IsNot Nothing Then
                    updateKeyWrite.SetValue("version", CurrentVersion.ToString())
                    updateKeyWrite.SetValue("skip", 0)
                End If
            End If
        End If

        updateKey.SetValue("lastcheck", DateTime.Now.ToString(CultureInfo.InvariantCulture))
        updateKey.Close()
        'RunBrowserThread(versionURL)

        '#If DEBUG Then
        '        If force Then
        '            args.Status = AutoUpdateEventArgs.StatusCodes.updateAvailable
        '            args.Text = Resources.sUpdateAvailable
        '            OnUpdateStatus(args)

        '            Dim thread__3 = New Thread(AddressOf ShowUI)
        '            thread__3.CurrentCulture = IIf(Not thread__3.CurrentUICulture Is Nothing, CurrentCulture, Application.CurrentCulture)
        '            thread__3.SetApartmentState(ApartmentState.STA)
        '            thread__3.Start()
        '            Return
        '        End If
        '#End If

        If CurrentVersion Is Nothing Then
            args.Status = AutoUpdateEventArgs.StatusCodes.noUpdateAvailable
            args.Text = Resources.sLatestVersion
            OnUpdateStatus(args)
            Return
        End If

        If CurrentVersion <= InstalledVersion Then
            Return
        End If

        args.Status = AutoUpdateEventArgs.StatusCodes.updateAvailable
        args.Text = Resources.sUpdateAvailable
        OnUpdateStatus(args)

        If days = 0 Then
            Dim thread__3 = New Thread(AddressOf ShowUI)
            thread__3.CurrentCulture = If(thread__3.CurrentUICulture IsNot Nothing, CurrentCulture, Application.CurrentCulture)
            thread__3.SetApartmentState(ApartmentState.STA)
            thread__3.Start()
        End If
    End Sub

    Private Shared Sub InvokeWebBrowser(ByVal url As String)
        Dim br As New WebBrowser()
        AddHandler br.DocumentCompleted, AddressOf browser_DocumentCompleted
        br.Navigate(url)
        Application.Run()
    End Sub

    Private Shared Sub RunBrowserThread(url As String)
        Dim th As New Thread(New ParameterizedThreadStart(AddressOf InvokeWebBrowser)) '
        th.SetApartmentState(ApartmentState.STA)
        th.Start(url)
    End Sub

    Private Shared Sub browser_DocumentCompleted(sender As Object, e As WebBrowserDocumentCompletedEventArgs)
        Dim br = TryCast(sender, WebBrowser)
        If br.Url = e.Url Then
            System.Diagnostics.Debug.WriteLine("Navigated to {0}" & vbCr & vbLf & "{1}", e.Url, br.DocumentText)
            versionResult = br.DocumentText
            Application.ExitThread()
        End If
    End Sub

    Private Shared Sub ShowUI()
        Dim updateForm As New UpdateForm()
        updateForm.ShowDialog()
    End Sub

    Private Shared Function GetAttribute(assembly As Assembly, attributeType As Type) As Attribute
        Dim attributes = assembly.GetCustomAttributes(attributeType, False)
        If attributes.Length = 0 Then
            Return Nothing
        End If
        Return DirectCast(attributes(0), Attribute)
    End Function


End Class
